Micron Document
🎖️GitЯра🎖️


Displaying Raw • Download

feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/DiscoveryHomeRestorer.kt bd2863243bab6eb213401d949839a2bc74dde7e2 (bd286324) Text, 20.50 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.feature.discovery

Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787kotlinx.coroutines.CancellationException
Tff7b72import T7ee787kotlinx.coroutines.CoroutineStart
Tff7b72import T7ee787kotlinx.coroutines.Deferred
Tff7b72import T7ee787kotlinx.coroutines.async
Tff7b72import T7ee787kotlinx.coroutines.currentCoroutineContext
Tff7b72import T7ee787kotlinx.coroutines.delay
Tff7b72import T7ee787kotlinx.coroutines.ensureActive
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableStateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.combine
Tff7b72import T7ee787kotlinx.coroutines.flow.first
Tff7b72import T7ee787kotlinx.coroutines.isActive
Tff7b72import T7ee787kotlinx.coroutines.sync.Mutex
Tff7b72import T7ee787kotlinx.coroutines.sync.withLock
Tff7b72import T7ee787kotlinx.coroutines.withTimeoutOrNull
Tff7b72import T7ee787org.meshtastic.core.common.di.ApplicationCoroutineScope
Tff7b72import T7ee787org.meshtastic.core.common.util.safeCatching
Tff7b72import T7ee787org.meshtastic.core.database.dao.DiscoveryDao
Tff7b72import T7ee787org.meshtastic.core.database.entity.DiscoverySessionEntity
Tff7b72import T7ee787org.meshtastic.core.database.entity.DiscoverySessionStatus
Tff7b72import T7ee787org.meshtastic.core.model.ConnectionState
Tff7b72import T7ee787org.meshtastic.core.repository.MeshPrefs
Tff7b72import T7ee787org.meshtastic.core.repository.RadioController
Tff7b72import T7ee787org.meshtastic.core.repository.ServiceRepository
Tff7b72import T7ee787org.meshtastic.proto.Channel
Tff7b72import T7ee787org.meshtastic.proto.ChannelSettings
Tff7b72import T7ee787org.meshtastic.proto.Config
Tff7b72import T7ee787kotlin.time.Duration.Companion.milliseconds
Tff7b72import T7ee787kotlin.time.Duration.Companion.seconds

Tff7b72internal Tff7b72data Tff7b72class T56d364DiscoveryHomeRestorePlanTb4b4b4(
Tff7b72val Te6edf3sessionIdTb4b4b4: Tffa657LongTb4b4b4,
Tff7b72val Te6edf3deviceAddressTb4b4b4: Tffa657String?Tb4b4b4,
Tff7b72val Te6edf3loraConfigTb4b4b4: Te6edf3ConfigTb4b4b4.Te6edf3LoRaConfigTb4b4b4,
Tff7b72val Te6edf3primaryChannelTb4b4b4: Te6edf3ChannelSettings?Tb4b4b4,
Tff7b72val Te6edf3restorePrimaryChannelTb4b4b4: Tffa657BooleanTb4b4b4,
Tff7b72val Te6edf3finalStatusTb4b4b4: Tffa657StringTb4b4b4,
Tb4b4b4)

Tff7b72private Tff7b72fun Te6edf3DiscoveryHomeRestorePlanTb4b4b4.Td2a8ffmatchesDeviceTb4b4b4(Te6edf3deviceAddressTb4b4b4: Tffa657String?Tb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72=
Tff7b72thisTb4b4b4.Te6edf3deviceAddress Tff7b72=Tff7b72= Te6edf3deviceAddress

Tff7b72internal Tff7b72fun Td2a8fffinalStatusForPendingRestoreTb4b4b4(
Te6edf3completionStatusTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3defaultTb4b4b4: Tffa657String Tff7b72= Te6edf3DiscoverySessionStatusTb4b4b4.Te6edf3RESTOREDTb4b4b4,
Tb4b4b4)Tb4b4b4: Tffa657String Tff7b72= Tff7b72when Tb4b4b4(Te6edf3completionStatusTb4b4b4) Tb4b4b4{
Te6edf3DiscoverySessionStatusTb4b4b4.Te6edf3RESTORE_PENDING_STOPPED Tff7b72-Tff7b72> Te6edf3DiscoverySessionStatusTb4b4b4.Te6edf3STOPPED
Te6edf3DiscoverySessionStatusTb4b4b4.Te6edf3RESTORE_PENDING_FAILED Tff7b72-Tff7b72> Te6edf3DiscoverySessionStatusTb4b4b4.Te6edf3FAILED
Te6edf3DiscoverySessionStatusTb4b4b4.Te6edf3RESTORE_PENDING_COMPLETE Tff7b72-Tff7b72> Te6edf3DiscoverySessionStatusTb4b4b4.Te6edf3COMPLETE
Tff7b72else Tff7b72-Tff7b72> Te6edf3default
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffawaitRestoreResultTb4b4b4(Te6edf3resultTb4b4b4: Te6edf3DeferredTff7b72<Tffa657BooleanTff7b72>Tb4b4b4, Te6edf3timeoutTb4b4b4: Te6edf3kotlinTb4b4b4.Te6edf3timeTb4b4b4.Te6edf3DurationTb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Tff7b72val Te6edf3completed Tff7b72=
Te6edf3withTimeoutOrNullTb4b4b4(Te6edf3timeoutTb4b4b4) Tb4b4b4{
T8b949e// A superseded restore cancels its Deferred while the foreground waiter remains valid. Inspect that
T8b949e// cancellation explicitly so only cancellation of this waiting coroutine propagates.
Tff7b72val Te6edf3attempt Tff7b72= Te6edf3runCatching Tb4b4b4{ Te6edf3resultTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4) Tb4b4b4}
Tff7b72val Te6edf3failure Tff7b72= Te6edf3attemptTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)
Tff7b72when Tb4b4b4{
Te6edf3failure Tff7b72=Tff7b72= Tff7b72null Tff7b72-Tff7b72> Te6edf3attemptTb4b4b4.Te6edf3getOrDefaultTb4b4b4(Tff7b72falseTb4b4b4)

Te6edf3failure Tff7b72is Te6edf3CancellationException Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3currentCoroutineContextTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3ensureActiveTb4b4b4(Tb4b4b4)
Tff7b72false
Tb4b4b4}

Tff7b72else Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3wTb4b4b4(Te6edf3failureTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffDiscoveryScanEngine: awaited home restore failedTa5d6ff" Tb4b4b4}
Tff7b72false
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tff7b72return Te6edf3completed Tff7b72=Tff7b72= Tff7b72true
Tb4b4b4}

Tff7b72internal Tff7b72suspend Tff7b72fun Td2a8ffawaitForegroundRestoreTb4b4b4(Te6edf3resultTb4b4b4: Te6edf3DeferredTff7b72<Tffa657BooleanTff7b72>Tb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72=
Te6edf3awaitRestoreResultTb4b4b4(Te6edf3resultTb4b4b4, Te6edf3DiscoveryHomeRestorerTb4b4b4.Te6edf3FOREGROUND_RESTORE_TIMEOUT_MSTb4b4b4.Te6edf3millisecondsTb4b4b4)

T8b949e/** Owns process-lifetime restoration of the radio configuration captured before a discovery scan. */
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooManyFunctionsTa5d6ff"Tb4b4b4)
Tff7b72internal Tff7b72class T56d364DiscoveryHomeRestorerTb4b4b4(
Tff7b72private Tff7b72val Te6edf3radioControllerTb4b4b4: Te6edf3RadioControllerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3serviceRepositoryTb4b4b4: Te6edf3ServiceRepositoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3discoveryDaoTb4b4b4: Te6edf3DiscoveryDaoTb4b4b4,
Tff7b72private Tff7b72val Te6edf3applicationScopeTb4b4b4: Te6edf3ApplicationCoroutineScopeTb4b4b4,
Tff7b72private Tff7b72val Te6edf3meshPrefsTb4b4b4: Te6edf3MeshPrefsTb4b4b4,
Tb4b4b4) Tb4b4b4{
T8b949e/** Guarded by [pendingMutex]. Retains a completed restore long enough to correct a racing terminal outcome. */
Tff7b72private Tff7b72class T56d364RestoreStateTb4b4b4(Tff7b72var Te6edf3finalStatusTb4b4b4: Tffa657StringTb4b4b4, Tff7b72var Te6edf3persistedFinalStatusTb4b4b4: Tffa657String? Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3completedSuccessfully Tff7b72= Te6edf3MutableStateFlowTff7b72<Tffa657Boolean?Tff7b72>Tb4b4b4(Tff7b72nullTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72data Tff7b72class T56d364RestoreRetryStateTb4b4b4(
Tff7b72var Te6edf3writeAttemptsTb4b4b4: Tffa657Int Tff7b72= T79c0ff0Tb4b4b4,
Tff7b72var Te6edf3ownershipRechecksRemainingTb4b4b4: Tffa657Int Tff7b72= Te6edf3MAX_OWNERSHIP_RECHECKSTb4b4b4,
Tff7b72var Te6edf3writeRetryDelayMsTb4b4b4: Tffa657Long Tff7b72= Te6edf3RETRY_DELAY_MSTb4b4b4,
Tb4b4b4)

Tff7b72private Tff7b72data Tff7b72class T56d364PendingRestoreTb4b4b4(
Tff7b72val Te6edf3planTb4b4b4: Te6edf3DiscoveryHomeRestorePlanTb4b4b4,
Tff7b72val Te6edf3resultTb4b4b4: Te6edf3DeferredTff7b72<Tffa657BooleanTff7b72>Tb4b4b4,
Tff7b72val Te6edf3stateTb4b4b4: Te6edf3RestoreStateTb4b4b4,
Tb4b4b4)

T8b949e// Lock order invariant: acquire persistenceMutex before pendingMutex. Never acquire persistenceMutex while holding
T8b949e// pendingMutex. pendingRestoreSnapshot mirrors the guarded reference for non-suspending admission rechecks only.
Tff7b72private Tff7b72val Te6edf3pendingMutex Tff7b72= Te6edf3MutexTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3persistenceMutex Tff7b72= Te6edf3MutexTb4b4b4(Tb4b4b4)
Tff7b72private Tff7b72var Te6edf3pendingRestoreTb4b4b4: Te6edf3PendingRestore? Tff7b72= Tff7b72null
Tff7b72private Tff7b72val Te6edf3pendingRestoreSnapshot Tff7b72= Te6edf3MutableStateFlowTff7b72<Te6edf3PendingRestore?Tff7b72>Tb4b4b4(Tff7b72nullTb4b4b4)

T8b949e/** Caller must hold [pendingMutex]. */
Tff7b72private Tff7b72fun Td2a8ffpublishPendingRestoreTb4b4b4(Te6edf3valueTb4b4b4: Te6edf3PendingRestore?Tb4b4b4) Tb4b4b4{
Te6edf3pendingRestore Tff7b72= Te6edf3value
Te6edf3pendingRestoreSnapshotTb4b4b4.Te6edf3value Tff7b72= Te6edf3value
Tb4b4b4}

T8b949e/** A same-device scan cannot retune until a previously scheduled home restore has succeeded. */
Tff7b72suspend Tff7b72fun Td2a8ffawaitBeforeScanTb4b4b4(Te6edf3deviceAddressTb4b4b4: Tffa657String?Tb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Tff7b72val Te6edf3pending Tff7b72= Te6edf3pendingMutexTb4b4b4.Te6edf3withLock Tb4b4b4{ Te6edf3pendingRestore Tb4b4b4}
Tff7b72return Tff7b72when Tb4b4b4{
Te6edf3pending Tff7b72=Tff7b72= Tff7b72null Tff7b72-Tff7b72> Tff7b72true

Te6edf3pendingTb4b4b4.Te6edf3planTb4b4b4.Te6edf3deviceAddress Tff7b72!Tff7b72= Te6edf3deviceAddress Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3pendingTb4b4b4.Te6edf3resultTb4b4b4.Te6edf3cancelTb4b4b4(Tb4b4b4)
Te6edf3pendingMutexTb4b4b4.Te6edf3withLock Tb4b4b4{ Tff7b72if Tb4b4b4(Te6edf3pendingRestore Tff7b72=Tff7b72=Tff7b72= Te6edf3pendingTb4b4b4) Te6edf3publishPendingRestoreTb4b4b4(Tff7b72nullTb4b4b4) Tb4b4b4}
Tff7b72true
Tb4b4b4}

Tff7b72else Tff7b72-Tff7b72> Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3pendingTb4b4b4.Te6edf3resultTb4b4b4.Te6edf3isCompletedTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffDiscoveryScanEngine: waiting for pending home restore before starting a new scanTa5d6ff" Tb4b4b4}
Tb4b4b4}
Tff7b72val Te6edf3restored Tff7b72= Te6edf3awaitRestoreResultTb4b4b4(Te6edf3pendingTb4b4b4.Te6edf3resultTb4b4b4, Te6edf3START_WAIT_TIMEOUTTb4b4b4)
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3restoredTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3pendingTb4b4b4.Te6edf3resultTb4b4b4.Te6edf3isCompletedTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3e Tb4b4b4{
Ta5d6ff"Ta5d6ffDiscoveryScanEngine: previous home restore did not complete successfully; Ta5d6ff" Tff7b72+
Ta5d6ff"Ta5d6ffrefusing a same-device scanTa5d6ff"
Tb4b4b4}
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffDiscoveryScanEngine: home restore is still pending; deferring new scanTa5d6ff" Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Te6edf3restored
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

T8b949e/**
* Non-suspending scan-engine recheck used while its mutex prevents a new recovery restore from racing admission. A
* completed restore remains blocking unless it completed successfully.
*/
Tff7b72fun Td2a8ffhasUnsatisfiedRestoreForTb4b4b4(Te6edf3deviceAddressTb4b4b4: Tffa657String?Tb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Tff7b72val Te6edf3pending Tff7b72= Te6edf3pendingRestoreSnapshotTb4b4b4.Te6edf3valueTff7b72?.Te6edf3takeIf Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3planTb4b4b4.Te6edf3deviceAddress Tff7b72=Tff7b72= Te6edf3deviceAddress Tb4b4b4}
Tff7b72return Te6edf3pendingTff7b72?.Te6edf3let Tb4b4b4{ Tff7b72!Tffa657itTb4b4b4.Te6edf3resultTb4b4b4.Te6edf3isCompleted Tff7b72|Tff7b72| Tffa657itTb4b4b4.Te6edf3stateTb4b4b4.Te6edf3completedSuccessfullyTb4b4b4.Te6edf3value Tff7b72!Tff7b72= Tff7b72true Tb4b4b4} Tff7b72?: Tff7b72false
Tb4b4b4}

T8b949e/** Registers a restore in the application scope. Repeated scheduling of the same active plan is idempotent. */
Tff7b72suspend Tff7b72fun Td2a8ffscheduleTb4b4b4(Te6edf3planTb4b4b4: Te6edf3DiscoveryHomeRestorePlanTb4b4b4)Tb4b4b4: Te6edf3DeferredTff7b72<Tffa657BooleanTff7b72> Tb4b4b4{
Tff7b72var Te6edf3supersededTb4b4b4: Te6edf3PendingRestore? Tff7b72= Tff7b72null
Tff7b72var Te6edf3created Tff7b72= Tff7b72false
Tff7b72val Te6edf3pending Tff7b72=
Te6edf3pendingMutexTb4b4b4.Te6edf3withLock Tb4b4b4{
Tff7b72val Te6edf3existing Tff7b72= Te6edf3pendingRestore
Tff7b72if Tb4b4b4(
Te6edf3existing Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72&
Tff7b72!Te6edf3existingTb4b4b4.Te6edf3resultTb4b4b4.Te6edf3isCompleted Tff7b72&Tff7b72&
Te6edf3existingTb4b4b4.Te6edf3planTb4b4b4.Te6edf3sessionId Tff7b72=Tff7b72= Te6edf3planTb4b4b4.Te6edf3sessionId Tff7b72&Tff7b72&
Te6edf3existingTb4b4b4.Te6edf3planTb4b4b4.Te6edf3deviceAddress Tff7b72=Tff7b72= Te6edf3planTb4b4b4.Te6edf3deviceAddress
Tb4b4b4) Tb4b4b4{
Te6edf3existing
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3superseded Tff7b72= Te6edf3existingTff7b72?.Te6edf3takeUnless Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3resultTb4b4b4.Te6edf3isCompleted Tb4b4b4}
Tff7b72val Te6edf3state Tff7b72= Te6edf3RestoreStateTb4b4b4(Te6edf3planTb4b4b4.Te6edf3finalStatusTb4b4b4)
Tff7b72val Te6edf3result Tff7b72=
Te6edf3applicationScopeTb4b4b4.Te6edf3asyncTb4b4b4(Te6edf3start Tff7b72= Te6edf3CoroutineStartTb4b4b4.Te6edf3LAZYTb4b4b4) Tb4b4b4{
Te6edf3restoreUntilCompleteTb4b4b4(Te6edf3planTb4b4b4, Te6edf3stateTb4b4b4)Tb4b4b4.Te6edf3also Tb4b4b4{ Te6edf3stateTb4b4b4.Te6edf3completedSuccessfullyTb4b4b4.Te6edf3value Tff7b72= Tffa657it Tb4b4b4}
Tb4b4b4}
Te6edf3PendingRestoreTb4b4b4(Te6edf3planTb4b4b4, Te6edf3resultTb4b4b4, Te6edf3stateTb4b4b4)Tb4b4b4.Te6edf3also Tb4b4b4{
Te6edf3publishPendingRestoreTb4b4b4(Tffa657itTb4b4b4)
Te6edf3created Tff7b72= Tff7b72true
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Te6edf3supersededTff7b72?.Te6edf3resultTff7b72?.Te6edf3cancelTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3createdTb4b4b4) Tb4b4b4{
Te6edf3pendingTb4b4b4.Te6edf3resultTb4b4b4.Te6edf3invokeOnCompletion Tb4b4b4{ Te6edf3cause Tff7b72-Tff7b72>
Tff7b72if Tb4b4b4(Te6edf3cause Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3cause Tff7b72!is Te6edf3CancellationExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3causeTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffDiscoveryScanEngine: background home restore failed unexpectedlyTa5d6ff" Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Te6edf3pendingTb4b4b4.Te6edf3resultTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tff7b72return Te6edf3pendingTb4b4b4.Te6edf3result
Tb4b4b4}

T8b949e/** Changes the status a running or just-completed restore publishes, correcting a racing terminal write. */
Tff7b72suspend Tff7b72fun Td2a8ffupdateFinalStatusTb4b4b4(Te6edf3sessionIdTb4b4b4: Tffa657LongTb4b4b4, Te6edf3finalStatusTb4b4b4: Tffa657StringTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3pending Tff7b72=
Te6edf3pendingMutexTb4b4b4.Te6edf3withLock Tb4b4b4{
Tff7b72val Te6edf3current Tff7b72= Te6edf3pendingRestoreTff7b72?.Te6edf3takeIf Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3planTb4b4b4.Te6edf3sessionId Tff7b72=Tff7b72= Te6edf3sessionId Tb4b4b4} Tff7b72?: Tff7b72returnTf0883e@withLock Tff7b72null
Te6edf3currentTb4b4b4.Te6edf3stateTb4b4b4.Te6edf3finalStatus Tff7b72= Te6edf3finalStatus
Te6edf3current
Tb4b4b4} Tff7b72?: Tff7b72return
Tff7b72val Te6edf3correction Tff7b72=
Te6edf3persistenceMutexTb4b4b4.Te6edf3withLock Tb4b4b4{
Tff7b72val Te6edf3shouldCorrect Tff7b72=
Te6edf3pendingMutexTb4b4b4.Te6edf3withLock Tb4b4b4{
Te6edf3pendingRestore Tff7b72=Tff7b72=Tff7b72= Te6edf3pending Tff7b72&Tff7b72&
Te6edf3pendingTb4b4b4.Te6edf3stateTb4b4b4.Te6edf3finalStatus Tff7b72=Tff7b72= Te6edf3finalStatus Tff7b72&Tff7b72&
Te6edf3pendingTb4b4b4.Te6edf3stateTb4b4b4.Te6edf3persistedFinalStatusTff7b72?.Te6edf3let Tb4b4b4{ Tffa657it Tff7b72!Tff7b72= Te6edf3finalStatus Tb4b4b4} Tff7b72=Tff7b72= Tff7b72true
Tb4b4b4}
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3shouldCorrectTb4b4b4) Tff7b72returnTf0883e@withLock Tff7b72null

Tff7b72val Te6edf3result Tff7b72= Te6edf3safeCatching Tb4b4b4{ Te6edf3discoveryDaoTb4b4b4.Te6edf3updateSessionCompletionStatusTb4b4b4(Te6edf3sessionIdTb4b4b4, Te6edf3finalStatusTb4b4b4) Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3resultTb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4) Tff7b72=Tff7b72= T79c0ff1Tb4b4b4) Tb4b4b4{
Te6edf3pendingMutexTb4b4b4.Te6edf3withLock Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3pendingRestore Tff7b72=Tff7b72=Tff7b72= Te6edf3pendingTb4b4b4) Te6edf3pendingTb4b4b4.Te6edf3stateTb4b4b4.Te6edf3persistedFinalStatus Tff7b72= Te6edf3finalStatus
Tb4b4b4}
Tb4b4b4}
Te6edf3result
Tb4b4b4}
Tff7b72val Te6edf3correctionFailure Tff7b72= Te6edf3correctionTff7b72?.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)
Tff7b72when Tb4b4b4{
Te6edf3correctionFailure Tff7b72!Tff7b72= Tff7b72null Tff7b72-Tff7b72>
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3correctionFailureTb4b4b4) Tb4b4b4{
Ta5d6ff"Ta5d6ffDiscoveryScanEngine: failed to correct the restored session's terminal statusTa5d6ff"
Tb4b4b4}

Te6edf3correction Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3correctionTb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4) Tff7b72!Tff7b72= T79c0ff1 Tff7b72-Tff7b72>
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffDiscoveryScanEngine: terminal status correction did not update session Tffd700$Te6edf3sessionIdTa5d6ff" Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

T8b949e/** Gives normal scan completion a bounded foreground opportunity while the process-lifetime job keeps running. */
Tff7b72suspend Tff7b72fun Td2a8ffawaitForegroundTb4b4b4(Te6edf3planTb4b4b4: Te6edf3DiscoveryHomeRestorePlanTb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72=
Te6edf3awaitRestoreResultTb4b4b4(Te6edf3scheduleTb4b4b4(Te6edf3planTb4b4b4)Tb4b4b4, Te6edf3FOREGROUND_RESTORE_TIMEOUT_MSTb4b4b4.Te6edf3millisecondsTb4b4b4)

T8b949e/** Registers a persisted interrupted/pending session restore without waiting for it to finish. */
Tff7b72suspend Tff7b72fun Td2a8ffschedulePersistedSessionTb4b4b4(Te6edf3sessionTb4b4b4: Te6edf3DiscoverySessionEntityTb4b4b4)Tb4b4b4: Te6edf3DeferredTff7b72<Tffa657BooleanTff7b72>Tff7b72? Tb4b4b4{
Tff7b72val Te6edf3loraConfig Tff7b72= Te6edf3sessionTb4b4b4.Te6edf3homeLoraConfig Tff7b72?: Tff7b72return Tff7b72null
Tff7b72return Te6edf3scheduleTb4b4b4(
Te6edf3DiscoveryHomeRestorePlanTb4b4b4(
Te6edf3sessionId Tff7b72= Te6edf3sessionTb4b4b4.Te6edf3idTb4b4b4,
Te6edf3deviceAddress Tff7b72= Te6edf3sessionTb4b4b4.Te6edf3deviceAddressTb4b4b4,
Te6edf3loraConfig Tff7b72= Te6edf3loraConfigTb4b4b4,
Te6edf3primaryChannel Tff7b72= Te6edf3sessionTb4b4b4.Te6edf3homePrimaryChannelTb4b4b4,
Te6edf3restorePrimaryChannel Tff7b72= Te6edf3sessionTb4b4b4.Te6edf3homePrimaryChannel Tff7b72!Tff7b72= Tff7b72nullTb4b4b4,
Te6edf3finalStatus Tff7b72= Te6edf3finalStatusForPendingRestoreTb4b4b4(Te6edf3sessionTb4b4b4.Te6edf3completionStatusTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffrestoreUntilCompleteTb4b4b4(Te6edf3planTb4b4b4: Te6edf3DiscoveryHomeRestorePlanTb4b4b4, Te6edf3stateTb4b4b4: Te6edf3RestoreStateTb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3planTb4b4b4.Te6edf3restorePrimaryChannel Tff7b72&Tff7b72& Te6edf3planTb4b4b4.Te6edf3primaryChannel Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3e Tb4b4b4{
Ta5d6ff"Ta5d6ffDiscoveryScanEngine: primary-channel restore required but no channel captured Ta5d6ff" Tff7b72+
Ta5d6ff"Ta5d6fffor session Tffd700${Te6edf3planTb4b4b4.Te6edf3sessionIdTffd700}Ta5d6ff; abandoning restoreTa5d6ff"
Tb4b4b4}
Te6edf3markUnrestorableAndReleaseBarrierTb4b4b4(Te6edf3planTb4b4b4)
Tff7b72return Tff7b72false
Tb4b4b4}
Tff7b72val Te6edf3retries Tff7b72= Te6edf3RestoreRetryStateTb4b4b4(Tb4b4b4)
Tff7b72var Te6edf3restored Tff7b72= Tff7b72false
Tff7b72var Te6edf3shouldContinue Tff7b72= Tff7b72true
Tff7b72while Tb4b4b4(
Te6edf3currentCoroutineContextTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3isActive Tff7b72&Tff7b72&
Te6edf3planTb4b4b4.Te6edf3matchesDeviceTb4b4b4(Te6edf3meshPrefsTb4b4b4.Te6edf3deviceAddressTb4b4b4.Te6edf3valueTb4b4b4) Tff7b72&Tff7b72&
Tff7b72!Te6edf3restored Tff7b72&Tff7b72&
Te6edf3shouldContinue
Tb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3awaitConnectedTb4b4b4(Te6edf3planTb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3attempt Tff7b72= Te6edf3safeCatching Tb4b4b4{ Te6edf3applyHomeConfigurationTb4b4b4(Te6edf3planTb4b4b4) Tb4b4b4}
Tff7b72val Te6edf3failure Tff7b72= Te6edf3attemptTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)
Te6edf3restored Tff7b72= Te6edf3attemptTb4b4b4.Te6edf3getOrDefaultTb4b4b4(Tff7b72falseTb4b4b4)
Te6edf3shouldContinue Tff7b72=
Tff7b72when Tb4b4b4{
Te6edf3restored Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3finalizeRecoveredSessionBestEffortTb4b4b4(Te6edf3planTb4b4b4.Te6edf3sessionIdTb4b4b4, Te6edf3stateTb4b4b4)
Tff7b72false
Tb4b4b4}

Te6edf3failure Tff7b72=Tff7b72= Tff7b72null Tff7b72-Tff7b72> Te6edf3retryAfterOwnershipRejectionTb4b4b4(Te6edf3planTb4b4b4, Te6edf3retriesTb4b4b4)

Tff7b72else Tff7b72-Tff7b72> Te6edf3retryAfterWriteFailureTb4b4b4(Te6edf3planTb4b4b4, Te6edf3retriesTb4b4b4, Te6edf3failureTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tff7b72return Te6edf3restored
Tb4b4b4}

T8b949e/** Gives transport and selected-device ownership a short window to converge without retrying forever. */
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffretryAfterOwnershipRejectionTb4b4b4(
Te6edf3planTb4b4b4: Te6edf3DiscoveryHomeRestorePlanTb4b4b4,
Te6edf3retriesTb4b4b4: Te6edf3RestoreRetryStateTb4b4b4,
Tb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3retriesTb4b4b4.Te6edf3ownershipRechecksRemaining Tff7b72=Tff7b72= Te6edf3MAX_OWNERSHIP_RECHECKSTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{
Ta5d6ff"Ta5d6ffDiscoveryScanEngine: home restore rejected by device ownership for session Ta5d6ff" Tff7b72+
Ta5d6ff"Tffd700${Te6edf3planTb4b4b4.Te6edf3sessionIdTffd700}Ta5d6ff; re-checking selectionTa5d6ff"
Tb4b4b4}
Tb4b4b4}
Tff7b72val Te6edf3shouldRetry Tff7b72= Te6edf3retriesTb4b4b4.Te6edf3ownershipRechecksRemaining Tff7b72> T79c0ff0
Tff7b72if Tb4b4b4(Te6edf3shouldRetryTb4b4b4) Tb4b4b4{
Te6edf3retriesTb4b4b4.Te6edf3ownershipRechecksRemainingTff7b72-Tff7b72-
Te6edf3delayTb4b4b4(Te6edf3RETRY_DELAY_MSTb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{
Ta5d6ff"Ta5d6ffDiscoveryScanEngine: home restore still lacks device ownership for session Ta5d6ff" Tff7b72+
Ta5d6ff"Tffd700${Te6edf3planTb4b4b4.Te6edf3sessionIdTffd700}Ta5d6ff; leaving it recoverable for a later connection changeTa5d6ff"
Tb4b4b4}
Tb4b4b4}
Tff7b72return Te6edf3shouldRetry
Tb4b4b4}

T8b949e/** Retries real radio writes only while this restore still owns the connected device. */
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffretryAfterWriteFailureTb4b4b4(
Te6edf3planTb4b4b4: Te6edf3DiscoveryHomeRestorePlanTb4b4b4,
Te6edf3retriesTb4b4b4: Te6edf3RestoreRetryStateTb4b4b4,
Te6edf3failureTb4b4b4: Te6edf3ThrowableTb4b4b4,
Tb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Tff7b72val Te6edf3stillOwnsConnectedDevice Tff7b72=
Te6edf3planTb4b4b4.Te6edf3matchesDeviceTb4b4b4(Te6edf3meshPrefsTb4b4b4.Te6edf3deviceAddressTb4b4b4.Te6edf3valueTb4b4b4) Tff7b72&Tff7b72&
Te6edf3serviceRepositoryTb4b4b4.Te6edf3connectionStateTb4b4b4.Te6edf3value Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3Connected
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3stillOwnsConnectedDeviceTb4b4b4) Tff7b72return Tff7b72true

Te6edf3retriesTb4b4b4.Te6edf3ownershipRechecksRemaining Tff7b72= Te6edf3MAX_OWNERSHIP_RECHECKS
Te6edf3retriesTb4b4b4.Te6edf3writeAttemptsTff7b72+Tff7b72+
Tff7b72val Te6edf3attemptsExhausted Tff7b72= Te6edf3retriesTb4b4b4.Te6edf3writeAttempts Tff7b72>Tff7b72= Te6edf3MAX_RESTORE_ATTEMPTS
Te6edf3LoggerTb4b4b4.Te6edf3wTb4b4b4(Te6edf3failureTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3attemptsExhaustedTb4b4b4) Tb4b4b4{
Ta5d6ff"Ta5d6ffDiscoveryScanEngine: final home restore attempt failedTa5d6ff"
Tb4b4b4} Tff7b72else Tb4b4b4{
Ta5d6ff"Ta5d6ffDiscoveryScanEngine: home restore attempt failed; retrying when availableTa5d6ff"
Tb4b4b4}
Tb4b4b4}
Tff7b72return Tff7b72if Tb4b4b4(Te6edf3attemptsExhaustedTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3e Tb4b4b4{
Ta5d6ff"Ta5d6ffDiscoveryScanEngine: home restore exhausted Tffd700$Te6edf3MAX_RESTORE_ATTEMPTSTa5d6ff attempts for session Ta5d6ff" Tff7b72+
Ta5d6ff"Tffd700${Te6edf3planTb4b4b4.Te6edf3sessionIdTffd700}Ta5d6ff; marking it unrestorableTa5d6ff"
Tb4b4b4}
Te6edf3markUnrestorableAndReleaseBarrierTb4b4b4(Te6edf3planTb4b4b4)
Tff7b72false
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3delayTb4b4b4(Te6edf3retriesTb4b4b4.Te6edf3writeRetryDelayMsTb4b4b4)
Te6edf3retriesTb4b4b4.Te6edf3writeRetryDelayMs Tff7b72=
Tb4b4b4(Te6edf3retriesTb4b4b4.Te6edf3writeRetryDelayMs Tff7b72* Te6edf3RETRY_BACKOFF_MULTIPLIERTb4b4b4)Tb4b4b4.Te6edf3coerceAtMostTb4b4b4(Te6edf3MAX_RETRY_DELAY_MSTb4b4b4)
Tff7b72true
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffawaitConnectedTb4b4b4(Te6edf3planTb4b4b4: Te6edf3DiscoveryHomeRestorePlanTb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3planTb4b4b4.Te6edf3matchesDeviceTb4b4b4(Te6edf3meshPrefsTb4b4b4.Te6edf3deviceAddressTb4b4b4.Te6edf3valueTb4b4b4)Tb4b4b4) Tff7b72return Tff7b72false
Tff7b72if Tb4b4b4(Te6edf3serviceRepositoryTb4b4b4.Te6edf3connectionStateTb4b4b4.Te6edf3value Tff7b72!is Te6edf3ConnectionStateTb4b4b4.Te6edf3ConnectedTb4b4b4) Tb4b4b4{
Te6edf3combineTb4b4b4(Te6edf3serviceRepositoryTb4b4b4.Te6edf3connectionStateTb4b4b4, Te6edf3meshPrefsTb4b4b4.Te6edf3deviceAddressTb4b4b4) Tb4b4b4{ Te6edf3stateTb4b4b4, Te6edf3address Tff7b72-Tff7b72>
Te6edf3state Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3Connected Tff7b72|Tff7b72| Tff7b72!Te6edf3planTb4b4b4.Te6edf3matchesDeviceTb4b4b4(Te6edf3addressTb4b4b4)
Tb4b4b4}
Tb4b4b4.Te6edf3first Tb4b4b4{ Tffa657it Tb4b4b4}
Tb4b4b4}
Tff7b72return Te6edf3planTb4b4b4.Te6edf3matchesDeviceTb4b4b4(Te6edf3meshPrefsTb4b4b4.Te6edf3deviceAddressTb4b4b4.Te6edf3valueTb4b4b4) Tff7b72&Tff7b72&
Te6edf3serviceRepositoryTb4b4b4.Te6edf3connectionStateTb4b4b4.Te6edf3value Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3Connected
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffapplyHomeConfigurationTb4b4b4(Te6edf3planTb4b4b4: Te6edf3DiscoveryHomeRestorePlanTb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Tff7b72val Te6edf3primaryChannel Tff7b72=
Te6edf3planTb4b4b4.Te6edf3primaryChannel
Tff7b72?.Te6edf3takeIf Tb4b4b4{ Te6edf3planTb4b4b4.Te6edf3restorePrimaryChannel Tb4b4b4}
Tff7b72?.Te6edf3let Tb4b4b4{ Te6edf3ChannelTb4b4b4(Te6edf3index Tff7b72= T79c0ff0Tb4b4b4, Te6edf3role Tff7b72= Te6edf3ChannelTb4b4b4.Te6edf3RoleTb4b4b4.Te6edf3PRIMARYTb4b4b4, Te6edf3settings Tff7b72= Tffa657itTb4b4b4) Tb4b4b4}
Tff7b72val Te6edf3restored Tff7b72=
Te6edf3radioControllerTb4b4b4.Te6edf3restoreLocalConfigurationTb4b4b4(
Te6edf3expectedDeviceAddress Tff7b72= Te6edf3planTb4b4b4.Te6edf3deviceAddressTb4b4b4,
Te6edf3config Tff7b72= Te6edf3ConfigTb4b4b4(Te6edf3lora Tff7b72= Te6edf3planTb4b4b4.Te6edf3loraConfigTb4b4b4)Tb4b4b4,
Te6edf3primaryChannel Tff7b72= Te6edf3primaryChannelTb4b4b4,
Tb4b4b4)
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3restoredTb4b4b4) Tff7b72return Tff7b72false
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffDiscoveryScanEngine: restored original LoRa config for session Tffd700${Te6edf3planTb4b4b4.Te6edf3sessionIdTffd700}Ta5d6ff" Tb4b4b4}
Te6edf3delayTb4b4b4(Te6edf3POST_RESTORE_SETTLE_DELAY_MSTb4b4b4)
Tff7b72return Tff7b72true
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8fffinalizeRecoveredSessionBestEffortTb4b4b4(Te6edf3sessionIdTb4b4b4: Tffa657LongTb4b4b4, Te6edf3stateTb4b4b4: Te6edf3RestoreStateTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3result Tff7b72=
Te6edf3persistenceMutexTb4b4b4.Te6edf3withLock Tb4b4b4{
Tff7b72val Te6edf3finalStatus Tff7b72= Te6edf3pendingMutexTb4b4b4.Te6edf3withLock Tb4b4b4{ Te6edf3stateTb4b4b4.Te6edf3finalStatus Tb4b4b4}
Tff7b72val Te6edf3persistence Tff7b72= Te6edf3safeCatching Tb4b4b4{
Te6edf3discoveryDaoTb4b4b4.Te6edf3updateRecoverableSessionCompletionStatusTb4b4b4(Te6edf3sessionIdTb4b4b4, Te6edf3finalStatusTb4b4b4)
Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3persistenceTb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4) Tff7b72=Tff7b72= T79c0ff1Tb4b4b4) Tb4b4b4{
Te6edf3pendingMutexTb4b4b4.Te6edf3withLock Tb4b4b4{ Te6edf3stateTb4b4b4.Te6edf3persistedFinalStatus Tff7b72= Te6edf3finalStatus Tb4b4b4}
Tb4b4b4}
Te6edf3persistence
Tb4b4b4}
Tff7b72val Te6edf3failure Tff7b72= Te6edf3resultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3failure Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3failureTb4b4b4) Tb4b4b4{
Ta5d6ff"Ta5d6ffDiscoveryScanEngine: radio restored but terminal session persistence failed; keeping recovery rowTa5d6ff"
Tb4b4b4}
Tb4b4b4} Tff7b72else Tff7b72if Tb4b4b4(Te6edf3resultTb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4) Tff7b72!Tff7b72= T79c0ff1Tb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{
Ta5d6ff"Ta5d6ffDiscoveryScanEngine: session Tffd700$Te6edf3sessionIdTa5d6ff was no longer recoverable; terminal status not writtenTa5d6ff"
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

T8b949e/**
* Makes an unrecoverable restore terminal in both durable and in-memory ownership. The admission barrier is
* released only after the terminal status is persisted; if persistence fails, the recoverable row and barrier
* remain available for a later recovery attempt.
*/
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffmarkUnrestorableAndReleaseBarrierTb4b4b4(Te6edf3planTb4b4b4: Te6edf3DiscoveryHomeRestorePlanTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3markUnrestorableBestEffortTb4b4b4(Te6edf3planTb4b4b4.Te6edf3sessionIdTb4b4b4)Tb4b4b4) Tff7b72return
Te6edf3pendingMutexTb4b4b4.Te6edf3withLock Tb4b4b4{ Tff7b72if Tb4b4b4(Te6edf3pendingRestoreTff7b72?.Te6edf3plan Tff7b72=Tff7b72= Te6edf3planTb4b4b4) Te6edf3publishPendingRestoreTb4b4b4(Tff7b72nullTb4b4b4) Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffmarkUnrestorableBestEffortTb4b4b4(Te6edf3sessionIdTb4b4b4: Tffa657LongTb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Tff7b72val Te6edf3result Tff7b72=
Te6edf3persistenceMutexTb4b4b4.Te6edf3withLock Tb4b4b4{
Te6edf3safeCatching Tb4b4b4{
Te6edf3discoveryDaoTb4b4b4.Te6edf3updateRecoverableSessionCompletionStatusTb4b4b4(
Te6edf3sessionIdTb4b4b4,
Te6edf3DiscoverySessionStatusTb4b4b4.Te6edf3UNRESTORABLETb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tff7b72val Te6edf3failure Tff7b72= Te6edf3resultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4)
Tff7b72return Tff7b72when Tb4b4b4{
Te6edf3failure Tff7b72!Tff7b72= Tff7b72null Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3failureTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffDiscoveryScanEngine: failed to persist unrestorable session statusTa5d6ff" Tb4b4b4}
Tff7b72false
Tb4b4b4}

Te6edf3resultTb4b4b4.Te6edf3getOrNullTb4b4b4(Tb4b4b4) Tff7b72!Tff7b72= T79c0ff1 Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{
Ta5d6ff"Ta5d6ffDiscoveryScanEngine: session Tffd700$Te6edf3sessionIdTa5d6ff was no longer recoverable; unrestorable not writtenTa5d6ff"
Tb4b4b4}
Tff7b72false
Tb4b4b4}

Tff7b72else Tff7b72-Tff7b72> Tff7b72true
Tb4b4b4}
Tb4b4b4}

Tff7b72internal Tff7b72companion Tff7b72object Tb4b4b4{
Tff7b72const Tff7b72val Te6edf3FOREGROUND_RESTORE_TIMEOUT_MS Tff7b72= T79c0ff9T79c0ff0Te6edf3_000L
Tff7b72const Tff7b72val Te6edf3RETRY_DELAY_MS Tff7b72= T79c0ff1Te6edf3_000L
Tff7b72internal Tff7b72const Tff7b72val Te6edf3MAX_RETRY_DELAY_MS Tff7b72= T79c0ff3T79c0ff0Te6edf3_000L
Tff7b72internal Tff7b72const Tff7b72val Te6edf3MAX_RESTORE_ATTEMPTS Tff7b72= T79c0ff7
Tff7b72internal Tff7b72const Tff7b72val Te6edf3MAX_OWNERSHIP_RECHECKS Tff7b72= T79c0ff1T79c0ff5
Tff7b72internal Tff7b72const Tff7b72val Te6edf3RETRY_BACKOFF_MULTIPLIER Tff7b72= T79c0ff2L
Tff7b72const Tff7b72val Te6edf3POST_RESTORE_SETTLE_DELAY_MS Tff7b72= T79c0ff3Te6edf3_000L
Tff7b72private Tff7b72val Te6edf3START_WAIT_TIMEOUT Tff7b72= T79c0ff1T79c0ff5.Te6edf3seconds
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.12s